package com.warm.spring.asyn;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @Date: 2020/3/31 16:38
 * @Author: LChuang
 * @Description: 异步编程, 通过下面两个注解
 *
 *  1. @EnableAsync 通过在配置类或者Main类上加@EnableAsync开启对异步方法的支持。
 *  2. @Async 可以作用在类上或者方法上，作用在类上代表这个类的所有方法都是异步方法
 */
@Configuration
@EnableAsync
public class AsynConfig implements AsyncConfigurer {

    /** 核心线程数线程数定义了最小可以同时运行的线程数量 */
    private static final int CORE_POOL_SIZE = 6;

    /** 当队列中存放的任务达到队列容量的时候，当前可以同时运行的线程数量变为最大线程数 */
    private static final int MAX_POOL_SIZE = 10;

    /** 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数，如果达到的话，信任就会被存放在队列中。 */
    private static final int QUEUE_CAPACITY = 100;

    /*
    队列已满并且当前同时运行的线程数达到最大线程数的时候，如果再有新任务过来会发生什么呢？

    Spring 默认使用 ThreadPoolExecutor.AbortPolicy:
        ThreadPoolExecutor 将抛出 RejectedExecutionException 来拒绝新来的任务 ，这代表你将丢失对这个任务的处理

    使用ThreadPoolExecutor.CallerRunsPolicy。
        当最大池被填满时，此策略为我们提供可伸缩队列。

    ThreadPoolTaskExecutor 饱和策略定义:
        ThreadPoolExecutor.AbortPolicy：抛出 RejectedExecutionException来拒绝新任务的处理。
        ThreadPoolExecutor.CallerRunsPolicy：调用执行自己的线程运行任务。不会丢失任务请求。但是这种策略会降低对于新任务提交速度，
                                             影响程序的整体性能。另外，这个策略喜欢增加队列容量。如果您的应用程序可以承受此延迟
                                             并且你不能任务丢弃任何一个任务请求的话，你可以选择这个策略。
        ThreadPoolExecutor.DiscardPolicy： 不处理新任务，直接丢弃掉。
        ThreadPoolExecutor.DiscardOldestPolicy： 此策略将丢弃最早的未处理的任务请求。
     */

    @Bean
    public Executor taskExecutor() {
        // Spring 默认配置是核心线程数大小为1，最大线程容量大小不受限制，队列容量也不受限制。
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数
        executor.setCorePoolSize(CORE_POOL_SIZE);
        // 最大线程数
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        // 队列大小
        executor.setQueueCapacity(QUEUE_CAPACITY);
        // 当最大池已满时，此策略保证不会丢失任务请求，但是可能会影响应用程序整体性能。
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setThreadNamePrefix("My ThreadPoolTaskExecutor-");
        executor.initialize();
        return executor;
    }

}
